﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace Extented.UI.Core.Native
{
    public class RenderPropertyChangedListenerContext : RenderPropertyContextBase
    {
        public new RenderPropertyChangedListener Factory { get { return base.Factory as RenderPropertyChangedListener; } }
        public DependencyPropertyDescriptor Descriptor { get; private set; }
        protected object CurrentDescriptorSource { get; private set; }
        protected string CurrentPropertyName { get; private set; }
        protected bool IsInitialized { get; private set; }
        public bool HasDescriptor { get { return Descriptor != null; } }
        public RenderPropertyChangedListenerContext(RenderPropertyChangedListener factory)
            : base(factory)
        {
        }
        protected override void AttachOverride(INamescope scope, IElementHost elementHost, IPropertyChangedListener listener, RenderTriggerContextBase context)
        {
            InitializeDescriptorSource();
            InitializeDescriptor();
            listener.SubscribeValueChangedAsync(this);
        }
        void InitializeDescriptorSource()
        {
            CurrentDescriptorSource = GetDescriptorSource(ElementHost);
        }
        public void SubscribeValue()
        {
            SubscribeValueChanged(CurrentDescriptorSource);
        }
        public void UnsubscribeValue()
        {
            UnsubscribeValueChanged(CurrentDescriptorSource);
        }
        protected override void DetachOverride()
        {
            PropertyChangedListener.Flush();
            PropertyChangedListener.UnsubscribeValueChanged(CurrentDescriptorSource, this);
            UnInitializeDescriptor();
            UnInitializeDescriptorSource();
        }
        public void InitializeDescriptor()
        {
            if (IsInitialized)
                return;
            InitializeDescriptorOverride();
            IsInitialized = true;
        }
        public void UnInitializeDescriptor()
        {
            Descriptor = null;
            IsInitialized = false;
        }
        public void UnInitializeDescriptorSource()
        {
            CurrentDescriptorSource = null;
        }
        protected virtual void InitializeDescriptorOverride()
        {
            if (CurrentDescriptorSource == null || (Equals(Factory.Property, null) && Equals(Factory.DependencyProperty, null)))
            {
                CurrentPropertyName = null;
                Descriptor = null;
                return;
            }
            var type = CurrentDescriptorSource.GetType();
            CurrentPropertyName = Factory.Property;
            Descriptor = Factory.DependencyProperty == null ? DependencyPropertyDescriptor.FromName(Factory.Property, type, type) : DependencyPropertyDescriptor.FromProperty(Factory.DependencyProperty, type);
        }
        protected virtual object GetDescriptorSource(IElementHost elementHost)
        {
            object result = null;
            bool throwIfNull = true;
            switch (Factory.ValueSource)
            {
                case RenderValueSource.DataContext:
                    result = (Factory.TargetName.With(x => Namescope.GetElement(x)) ?? Namescope.RootElement).With(x => x.DataContext);
                    throwIfNull = false;
                    break;
                case RenderValueSource.ElementName:
                    result = Factory.SourceName.With(x => Namescope.GetElement(x));
                    break;
                case RenderValueSource.TemplatedParent:
                    result = elementHost.TemplatedParent;
                    throwIfNull = false;
                    break;
                default:
                    throw new NotImplementedException();
            }
            if (result == null && throwIfNull)
                throw new ArgumentException(String.Format("Cannot find element with name '{0}'", Factory.TargetName));
            else
                return result;
        }
        protected void SubscribeValueChanged(object target)
        {
            CurrentDescriptorSource = target;
            if (target == null)
                return;
            PropertyChangedListener.SubscribeValueChanged(target, this);
        }
        protected void UnsubscribeValueChanged(object target)
        {
            if (target == null)
                return;
            PropertyChangedListener.UnsubscribeValueChanged(target, this);
        }
        public virtual void PreviewValueChanged(object sender, EventArgs args) { }
        public virtual void ValueChanged(object sender, EventArgs args)
        {
            TriggerContext.Invalidate();
        }
        public object GetValue()
        {
            return Descriptor.Return(x => x.GetValue(CurrentDescriptorSource), () => CurrentDescriptorSource.If(s => Factory.Property == null && Factory.DependencyProperty == null));
        }
        public override void Reset()
        {
            if (GetDescriptorSource(ElementHost) == CurrentDescriptorSource)
                return;
            base.Reset();
        }
    }
}
